package com.turbo.cloud.core.utils;


import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.ThreadLocalRandom;

/**
 * @author zhangluning
 * @since 2020-07-15 7:22 上午
 * 使用方法： IdUtil.id("FU-CLS-", "P")
 * IdUtil.id("FU-","CLS-","P-")
 */
public final class CustomizerIdUtil {
    /**
     * 10进制数对应32位的码表，仅使用了前32位，包含0-9，A-Z（不包含a-z,'I'，'O', 'B', 'U'）
     */
    final private static char[] DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'L', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'I', 'O', 'B', 'U'};
    /**
     * 序列计数最大值为25位二进制，即：0x1FFFFFFl
     */
    private static Long counter = 0x0L;
    private static LocalDateTime localDateTime = LocalDateTime.now();
    /**
     * 服务器启动时的标识，按照startHMS = ss * 7 + mm * 3 + hh * 11生成，被乘数为质数。
     * 最大值为10位二进制，即：0x3FF
     */
    private static StringBuilder hhmmssHex;
    private static int BOUND = 30;
    private static long randomLong = ThreadLocalRandom.current().nextInt(BOUND);

    static {
        long startHMS = localDateTime.getSecond();
        /**
         * 将高于右侧三位的置为0
         */
        startHMS = startHMS & 0B000111;
        /**
         * 生成 127（0B1111111）以内的随机数，填充高于右侧三位的值
         */
        startHMS = startHMS | (ThreadLocalRandom.current().nextInt(0B1111111) << 3);
        hhmmssHex = new StringBuilder(hex10to32(startHMS));
        while (hhmmssHex.length() < 2) {
            hhmmssHex.insert(0, "0");
        }
    }

    private CustomizerIdUtil() {

    }

    public static String id(String className, String systemName, String category) {
        className = Objects.requireNonNull(className, "类名不能为空").toUpperCase();
        systemName = Objects.requireNonNull(systemName, "系统名称不能为空").toUpperCase();
        category = Objects.requireNonNull(category, "分类名称不能为空").toUpperCase();
        return id(className + systemName + category, null);
    }

    public static synchronized String id(String prefix, String suffix) {
        StringBuilder result = new StringBuilder(36);
        if (prefix == null) {
            prefix = "";
        }
        if (suffix == null) {
            suffix = "";
        }
        /*
         * 生成三位随机数填充二进制43-45位，不可超过第45位否则将产生升位
         */


        if (counter > 0x1FFFFFFL) {
            counter = 0L;
        }
        long nHex = System.currentTimeMillis();
        randomLong = randomLong > BOUND ? 1 : ++randomLong;
        nHex = nHex | (randomLong << 45);
        String newHex = hex10to32(nHex);
        StringBuilder counterHex = new StringBuilder(hex10to32(counter++));
        while (counterHex.length() < 5) {
            counterHex.insert(0, "0");
        }
        result.append(prefix).append(newHex).append(hhmmssHex).append(counterHex).append(suffix);
//        System.out.println(result+" ,prefix = "+prefix+",randomLong = "+randomLong+",current = "+hex10to32(System.currentTimeMillis())+",newHex = "+newHex+",hhmmssHex = "+hhmmssHex+",counterHex = "+counterHex+",suffix = "+suffix);
        return result.toString();
    }

    public static String hex10to32(long value) {
        char[] buf = new char[32];
        int charPos = 32;
        // 0b100000
        int radix = 1 << 5;
        // 0b11111
        long mask = radix - 1L;
        do {
            buf[--charPos] = DIGITS[(int) (value & mask)];
            value >>>= 5;
        } while (value != 0);
        return new String(buf, charPos, (32 - charPos));
    }

    public static long hex32to10(String value) {
        int length = value.length();
        long result = 0;
        for (int i = 0; i < length; i++) {
            char c = value.charAt(i);
            result = result << 5;
            result = result | Arrays.binarySearch(DIGITS, c);
        }
        return result;
    }
}
